package com.example.qibla_v2;



import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity implements SensorEventListener{

	SensorManager sensorManager;
	private Sensor sensorAccelerometer;
	private Sensor sensorMagneticField;

	private float[] valuesAccelerometer;
	private float[] valuesMagneticField;

	private float[] matrixR;
	private float[] matrixI;
	private float[] matrixValues;

	TextView readingAzimuth, readingPitch, readingRoll;
	CompassView myCompass;

	LocationManager locationManager;
	LocationListener locationListener;
	float bearing ;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		

		myCompass = (CompassView)findViewById(R.id.mycompass);

		sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
		sensorAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		sensorMagneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

		valuesAccelerometer = new float[3];
		valuesMagneticField = new float[3];

		matrixR = new float[9];
		matrixI = new float[9];
		matrixValues = new float[3];

		/*Acquire a reference to the system Location Manager*/
		locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
		
		if (false == locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){
			Log.e("QiblaTag", "NETWORK provider is not enabled");
		}else{
			Log.i("QiblaTag", "NETWORK provider is enabled");
		}
		
		if (false == locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
			Log.e("QiblaTag", "GPS provider is not enabled");
		}else {
			Log.i("QiblaTag", "GPS provider is enabled");
		}
		
		/*Define a listener that responds to location updates*/
		locationListener = new LocationListener() {

			public void onStatusChanged(String provider, int status, Bundle extras) {}

			public void onProviderEnabled(String provider) {
				Log.i("QiblaTag", "provider " + provider + " is enabled");
			}

			public void onProviderDisabled(String provider) {
				Log.i("QiblaTag", "provider " + provider + " is disabled");
			}

			@Override
			public void onLocationChanged(Location location) {
				
				Log.i("QiblaTag", "inside onLocationChanged");
				
				Log.i("QiblaTag","myLatitude = "+location.getLatitude() + " degree");
				Log.i("QiblaTag","myLongitude = "+location.getLongitude() + " degree");
				
				/*Get the Latitude & Longitude in radians since FloatMath.sin/cos uses radians format */
				double myLatitude 	= Math.toRadians(location.getLatitude()) ;
				double myLongitude  = Math.toRadians(location.getLongitude()) ;
				
				/*Called when a new location is found by the network location provider.*/
				calculateBearing(myLatitude, myLongitude);			
			}
		};
	}

	@Override
	protected void onResume() {

		sensorManager.registerListener(this,
				sensorAccelerometer,
				SensorManager.SENSOR_DELAY_NORMAL);
		sensorManager.registerListener(this,
				sensorMagneticField,
				SensorManager.SENSOR_DELAY_NORMAL);

		/*Register the listener with the Location Manager to receive location updates*/
		locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

		super.onResume();
	}

	@Override
	protected void onPause() {

		sensorManager.unregisterListener(this,
				sensorAccelerometer);
		sensorManager.unregisterListener(this,
				sensorMagneticField);

		/*Remove the listener you previously added*/
		locationManager.removeUpdates(locationListener);
		super.onPause();
	}

	@Override
	public void onAccuracyChanged(Sensor arg0, int arg1) {
		/*TODO: Can be used for calibration of compass or location determination */ 

	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		
		switch(event.sensor.getType()){
		case Sensor.TYPE_ACCELEROMETER:
			valuesAccelerometer = event.values.clone();
			smoothAccelometerData();
			break;
		case Sensor.TYPE_MAGNETIC_FIELD:
			valuesMagneticField = event.values.clone();
			break;

		}

		boolean success = SensorManager.getRotationMatrix(
				matrixR,
				matrixI,
				valuesAccelerometer,
				valuesMagneticField);

		if(success){
			SensorManager.getOrientation(matrixR, matrixValues);

			/*double azimuth = Math.toDegrees(matrixValues[0]);
			double pitch = Math.toDegrees(matrixValues[1]);
			double roll = Math.toDegrees(matrixValues[2]);*/

			float filteredAzimuth = restrictAngle(matrixValues[0]);
			filteredAzimuth = calculateFilteredAngle(matrixValues[0],filteredAzimuth);
			
			myCompass.update(filteredAzimuth, bearing);
		}

	}


	private float calculateBearing(double myLatitude, double myLongitude) {

		Log.i("QiblaTag", "inside calculate bearing");
		
		/*TODO: Try to use BearingTo Method.*/
		/*bearing = location.bearingTo()*/

		/* I am sure that angle radian unit ranges from 0 to 2*pi=6.28, so these given values must be degree.
		 * Since Mekkah is on east to Prime Meridian so it must have positive sign Longitude*/
		double MEKKAH_LONGTITUDE = -39.8230 ;
		double MEKKAH_LATITUDE   = 21.42330;

		/* since FloatMath takes angle in radians only so there must be trasnformation from degree into radians */
		float longtitudeMekkahInRadians = (float)Math.toRadians(MEKKAH_LONGTITUDE);
		float latitudeMekkahInRadians   = (float)Math.toRadians(MEKKAH_LATITUDE);
		
		double lonDelta =  (myLongitude - longtitudeMekkahInRadians);
		
		float longDeltaFloat = (float)lonDelta;
		float y = FloatMath.sin(longDeltaFloat) * FloatMath.cos(latitudeMekkahInRadians);
		float x = FloatMath.cos((float)myLatitude) * FloatMath.sin(latitudeMekkahInRadians) - 
					FloatMath.sin((float)myLatitude) * FloatMath.cos(latitudeMekkahInRadians) * FloatMath.cos(longDeltaFloat);

		bearing = (float) Math.atan2(y, x);
		Log.i("QiblaTag", "bearing in radians = " + bearing);
		
		/*bearing = (float) Math.toDegrees(bearing);
		Log.i("QiblaTag", "bearing in degree = "+ bearing);*/
		
		return bearing;
	}
	
	private void smoothAccelometerData (){
		final float alpha = (float) 0.8;

		/* smoothen the sensor data with the low-pass filter. */
		valuesAccelerometer[0] = alpha * valuesAccelerometer[0] + (1 - alpha) * valuesAccelerometer[0];
		valuesAccelerometer[1] = alpha * valuesAccelerometer[1] + (1 - alpha) * valuesAccelerometer[1];
		valuesAccelerometer[2] = alpha * valuesAccelerometer[2] + (1 - alpha) * valuesAccelerometer[2];
	}
	
	private float restrictAngle(float tmpAngle){
	    while(tmpAngle>=180) tmpAngle-=360;
	    while(tmpAngle<-180) tmpAngle+=360;
	    return tmpAngle;
	}

	//x is a raw angle value from getOrientation(...)
	//y is the current filtered angle value
	private float calculateFilteredAngle(float x, float y){ 
	    final float alpha = 0.1f;
	    float diff = x-y;

	    //here, we ensure that abs(diff)<=180
	    diff = restrictAngle(diff);

	    y += alpha*diff;
	    //ensure that y stays within [-180, 180[ bounds
	    y = restrictAngle(y);

	    return y;
	}
	
	
}